package com.fox.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.fox.stock.mapper.StockBlockRtInfoMapper;
import com.fox.stock.mapper.StockMarketIndexInfoMapper;
import com.fox.stock.mapper.StockOuterMarketIndexInfoMapper;
import com.fox.stock.mapper.StockRtInfoMapper;
import com.fox.stock.pojo.domain.*;
import com.fox.stock.pojo.vo.StockInfoConfig;
import com.fox.stock.service.StockService;
import com.fox.stock.utils.DateTimeUtil;
import com.fox.stock.pojo.vo.resq.PageResult;
import com.fox.stock.pojo.vo.resq.R;
import com.fox.stock.pojo.vo.resq.ResponseCode;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.base.AbstractInstant;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;


@Service("stockService")
@Slf4j
public class StockServiceImpl implements StockService {

    @Autowired
    private Cache<String,Object> caffenineCache;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;



    /**
     * 国内大盘
     */
    @Override
    public R<List<InnerMarketDomain>> getInnerMarketInfo() {


        R<List<InnerMarketDomain>> result = (R<List<InnerMarketDomain>>)caffenineCache.get("innerMarketQueue", key ->{
            // 1.获取国内A股大盘的Id集合
            List<String> inner = stockInfoConfig.getInner();
            // 2. 获取最近股票交易日期
            Date date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();

//            //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//            date = DateTime.parse("2022-01-02 09:32:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

            // 3. 将数据传入mapper层查询
            List<InnerMarketDomain> list = stockMarketIndexInfoMapper.getInnerMarketInfo(inner,date);
            // 4. 返回结果
            return R.ok(list);
        });

        return result;

    }

    /**
     * 获取国外大盘数据
     * @return
     */
    @Override
    public R<List<OutMarketDomain>> getOutMarketInfo() {
        // 获取国外大盘code集合
        List<String> outer = stockInfoConfig.getOuter();
        // 获取当前时间
        Date time = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();

            //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
            time = DateTime.parse("2025-10-12 22:51:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 调用Mapper层获取数据
        List<OutMarketDomain> outMarketDomain= stockOuterMarketIndexInfoMapper.getOutMarketInfo(outer,time);


        return R.ok(outMarketDomain);
    }

    /**
     * 根据输入的模糊个股代码,返回证券代码和名称
     * @param searchStr
     * @return
     */
    @Override
    public R<List<Map>> autoSearchByRtCode(String searchStr) {
        // 调用mapper层模糊查询
        List<Map> lists = stockRtInfoMapper.getCodeAndNameBySearchStr(searchStr);
        return R.ok(lists);
    }

    /**
     * 个股主营业务查询
     * @param code
     * @return
     */
    @Override
    public R<StockRtInfoDomain> getRtInfoMainWork(String code) {
        // 肯定合法
        // 直接调用mapper层查询数据
        StockRtInfoDomain stockRtInfoDomain = stockRtInfoMapper.getRtInfoMainWork(code);
        return R.ok(stockRtInfoDomain);
    }

    /**
     * 统计每周内的股票数据信息
     * @param code
     * @return
     */
    @Override
    public R<List<WeekKLineByRtInfoDomain>> getWeekKLineInfo(String code) {
        // 获取这一周的收盘时间集合
        List<DateTime> closeDatesByWeek = DateTimeUtil.getCloseDatesByWeek(DateTime.now());
        List<Date> dates = new ArrayList<>();
        for (DateTime dateTime : closeDatesByWeek) {
            dates.add(dateTime.toDate());
        }
        // 获取这周一的开盘时间
        DateTime dateTime1 =DateTimeUtil.getOpenDateByWeek1(DateTime.now());
        Date openDateByWeek1 = dateTime1.toDate();
        // 获取这周五的收盘时间(没到就以当前时间)
        DateTime dateTime2 = DateTimeUtil.getCloseDateByWeek5(DateTime.now());
        Date closeDateByWeek5 = dateTime2.toDate();
        // 直接调用mapper层获取这周内的股票信息
        List<WeekKLineByRtInfoDomain> date = new ArrayList<>();
        date.add(this.getWeekKLineInfo(dates,openDateByWeek1,closeDateByWeek5,code));
        for (int i = 1; i < 24; i++) {
            List<DateTime> list = DateTimeUtil.getCloseDatesByWeek(dateTime1.minusWeeks(i-1).minusDays(1));
            List<Date> dateList = list.stream().map(AbstractInstant::toDate).collect(Collectors.toList());
            openDateByWeek1 = dateTime1.minusWeeks(i).toDate();
            closeDateByWeek5 = DateTimeUtil.getLastDate4Stock(dateTime1.minusWeeks(i).plusDays(5)).withHourOfDay(15).withMinuteOfHour(0).withSecondOfMinute(0).withMillisOfSecond(0).toDate();
            WeekKLineByRtInfoDomain weekKLineInfo = this.getWeekKLineInfo(dateList, openDateByWeek1, closeDateByWeek5, code);
            if (weekKLineInfo == null) continue;
            date.add(weekKLineInfo);
        }
        return R.ok(date);
    }
    public WeekKLineByRtInfoDomain getWeekKLineInfo(List<Date> dates, Date openDateByWeek1, Date closeDateByWeek5, String code){
        Map<String,Object> map = stockRtInfoMapper.getWeekKLineInfo(dates, code);
        // 添加空值检查
        if (map == null) {
            return null; // 或者返回一个默认对象
        }
        WeekKLineByRtInfoDomain weekKLineInfo = WeekKLineByRtInfoDomain.builder()
                .stockCode((String) map.get("stockCode"))
                .minPrice((BigDecimal) map.get("minPrice"))
                .maxPrice((BigDecimal) map.get("maxPrice"))
                .avgPrice((BigDecimal) map.get("avgPrice"))
                .mxTime(closeDateByWeek5)
                .openPrice(stockRtInfoMapper.getOpenPriceByWeek1(openDateByWeek1,code))
                .closePrice(stockRtInfoMapper.getCurTimeByWeek5(closeDateByWeek5,code)).build();
        return weekKLineInfo;
    }

    /**
     * 获取个股最新分时行情数据
     * @param code
     * @return
     */
    @Override
    public R<StockRtDomain> getStockRtDetailInfo(String code) {
        // 1. 获取最新的交易时间
        Date date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();

        // 2. 将股票编码和当前时间传入mapper层查询数据
        StockRtDomain stockRtDomain = stockRtInfoMapper.getStockRtDetailInfo(code,date);

        // 3. 判断数据是否异常
        if(stockRtDomain == null){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        // 4. 正常返回
        return R.ok(stockRtDomain);
    }

    /**
     * 查询最新交易流水(前十条)
     * @param code
     * @return
     */
    @Override
    public R<List<Map<String,Object>>> getTradeDetailPre10Info(String code) {
        // 1. 不用获取额外数据,直接调用mapper层查询
        List<Map<String,Object>> list = stockRtInfoMapper.getTradeDetailPre10Info(code);
        // 2. 判断数据是否异常
        if (CollectionUtils.isEmpty(list)){
            return R.error(ResponseCode.NO_RESPONSE_DATA);
        }
        // 3. 正常返回
        return R.ok(list);
    }

    /**
     * 国内板块
     */
    @Override
    public R<List<StockBlockDomain>> getInnerSectorIndexInfo() {

            //1. 获取最近的交易时间
            Date date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();

//            //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//            date = DateTime.parse("2021-12-21 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

            //2. 将数据传入mapper层查询
            List<StockBlockDomain> list = stockBlockRtInfoMapper.getInnerSectorIndexInfo(date);

            //3. 将查好的数据返回
            if(CollectionUtils.isEmpty(list)){
                return R.error(ResponseCode.NO_RESPONSE_DATA);
            }
            return R.ok(list);
    }

    /**
     * 获取涨跌数据
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult<StockUpdownDomain>> getStockUpDownByPageHlper(Integer page, Integer pageSize) {
        // 1. 获取当前最新的股票交易时间
        Date date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();
        // 2. 配置pageHelper
        PageHelper.startPage(page, pageSize);
//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        date = DateTime.parse("2022-06-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 3. 调用mapper接口查询
        List<StockUpdownDomain> list = stockRtInfoMapper.getStockUpDownByPageHlper(date);

        // 4. 封装pageInfo
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(list);
        PageResult<StockUpdownDomain> r = new PageResult<>(pageInfo);

        // 返回结果
        return R.ok(r);
    }

    /**
     *   获取涨幅榜数据(前四)
     */
    @Override
    public R<List<StockUpdownDomain>> getStockUpDownList() {
        // 1. 获取当前最近股份交易时间
        Date date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1).toDate();

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        date = DateTime.parse("2022-06-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 调用Mapper接口查询涨幅榜前四的数据
        List<StockUpdownDomain> list = stockRtInfoMapper.getStockUpDownList(date);
        // 3. 返回结果
        return R.ok(list);
    }

    /**
     * 查询涨跌停数据
     */
    @Override
    public R<Map<String, List>> getStockUpDownCount() {
        // 1. 获取当前最近股份交易时间
        DateTime date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        date = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        // 2. 获取最近开盘时间
        Date openDate = DateTimeUtil.getOpenDate(date).minusMinutes(1).toDate();
        Date endDate = date.toDate();

        // 3. 调用mapper层查询涨停数据
        List<Map> up = stockRtInfoMapper.getStockUpDownCount(openDate,endDate,1);

        // 4. 调用mapper层查询跌停数据
        List<Map> down = stockRtInfoMapper.getStockUpDownCount(openDate,endDate,0);

        // 5. 封装查询数据
        Map<String, List> map = new HashMap<>();
        map.put("upList",up);
        map.put("downList",down);

        // 6. 返回结果
        return R.ok(map);

    }

    /**
     * 涨跌榜数据导出
     * @param page
     * @param pageSize
     * @param response
     */
    @Override
    public void StockExport(Integer page, Integer pageSize, HttpServletResponse response) {
        // 1. 获取当前最近股份交易时间
        DateTime date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        date = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date time = date.toDate();
        // 2. 获取数据
        R<PageResult<StockUpdownDomain>> stock = this.getStockUpDownByPageHlper(page, pageSize);
        List<StockUpdownDomain> data = stock.getData().getRows();
        if(data == null){
            log.info("当前时间{},当前页数{},每页大小{}",time,page,pageSize);
            return;
        }
        // 3. 导出数据
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        try {
            String fileName = URLEncoder.encode("股票信息表", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), StockUpdownDomain.class).sheet("股票涨幅信息").doWrite(data);
        } catch (IOException e) {
            log.info("当前导出数据异常，当前页：{},每页大小：{},异常信息：{}",page,pageSize,e.getMessage());
        }


    }


    /**
     * 统计A股大盘T日和T-1日成交量对比
     */
    @Override
    public R<Map<String, List>> stockTradeVol4InnerMarket() {
        // 1. 获取T日的开始时间和结束时间
        DateTime dateTime = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);
        Date enddate = dateTime.toDate();
        Date startdate = DateTimeUtil.getOpenDate(dateTime).minusMinutes(1).toDate();

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        enddate = DateTime.parse("2022-01-03 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
//        startdate =  DateTime.parse("2022-01-03 9:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 获取T-1日的开始时间和结束时间
        DateTime preEndDate = DateTimeUtil.getPreviousTradingDay(dateTime);
        Date preEnd = preEndDate.toDate();
        Date preStart = DateTimeUtil.getOpenDate(preEndDate).minusMinutes(1).toDate();

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        preStart = DateTime.parse("2022-01-02 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
//        preEnd = DateTime.parse("2022-01-02 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 3. 调用mapper层查询结果
        // T日
        List<Map> date = stockMarketIndexInfoMapper.getStockTradeVol4InnerMarket(startdate,enddate,stockInfoConfig.getInner());
        // T-1日
        List<Map> predate = stockMarketIndexInfoMapper.getStockTradeVol4InnerMarket(preStart,preEnd,stockInfoConfig.getInner());


        // 4. 封装数据
        Map<String, List> map = new HashMap<>();
        map.put("amtList",date);
        map.put("yesAmtList",predate);

        // 5. 返回响应结果
        return R.ok(map);

    }

    /**
     * 获取股票涨跌区间信息
     */
    @Override
    public R<Map<String, Object>> getPriceRangeInfo() {
        // 1. 获取当前最近交易的时间
        DateTime date = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);
//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        date = DateTime.parse("2022-01-06 09:55:0", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date time = date.toDate();

        // 2. 调用mapper层获取当前时间的涨跌区间
        List<Map> list = stockRtInfoMapper.getPriceRangeInfo(time);

        // 解决排序和0不显示问题
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
        List<Map> list1 = new ArrayList<>();
        for (String s : upDownRange) {
            boolean flag = true;
            for (Map map : list) {
                if(map.containsValue(s)){
                    list1.add(map);
                    flag = false;
                    break;
                }
            }
            if (flag){
                Map map = new HashMap<>();
                map.put("title",s);
                map.put("count",0);
                list1.add(map);
            }
        }

        // 3. 封装
        Map<String,Object> map = new HashMap<>();
        map.put("time",date.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
        map.put("infos",list1);

        // 4. 返回结果
        return R.ok(map);
    }

    /**
     * 查询个股分时信息的服务层
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String code) {
        // 1. 获取最近交易时间点,组成交易区间
        DateTime dateTime = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);
//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        dateTime = DateTime.parse("2022-01-06 09:55:0", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        Date startDate = DateTimeUtil.getOpenDate(dateTime).minusMinutes(1).toDate();
        Date endDate = dateTime.toDate();

        // 2. 调用mapper层查询个股分时信息
        List<Stock4MinuteDomain> list = stockRtInfoMapper.getStockScreenTimeSharing(code,startDate,endDate);

        // 3. 组装数据(buyong)

        // 4. 返回数据
        return R.ok(list);
    }

    /**
     * 获取个股的日分k线信息
     * @param code
     * @return
     */
    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenDayTimeSharing(String code) {
        // 1. 获取个股的k线显示多少天的区间
        DateTime endDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).minusMinutes(1);

//        //TODO mock测试数据，后期数据通过第三方接口动态获取实时数据 可删除
//        dateTime = DateTime.parse("2022-06-06 14:25:0", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        DateTime startDate = endDate.minusMonths(6);
//         2. 调用mapper接口查询个股再时间区间内的日k线信息
//        List<Stock4EvrDayDomain> list = stockMarketIndexInfoMapper.getStockScreenDayTimeSharing(startDate,endDate,code);

//         拆分sql
        // 2.1 获取个股在时间区间内的每天最后的交易时间列表
        // 先获取所有区间内的收盘时间(如果当天没到收盘则就是最近时间)
        HashSet<Date> lastTimeByDay = new HashSet<>();
        while(DateTimeUtil.isBefore(startDate,endDate)){
            lastTimeByDay.add(DateTimeUtil.getCloseDate(startDate).minusMinutes(1).toDate());
            startDate = startDate.plusDays(1);
        }
        lastTimeByDay.add(DateTimeUtil.getCloseDate(endDate).minusMinutes(1).toDate());
//        List<Date> listDate = stockRtInfoMapper.getLastTimeByDayInfo(startDate,endDate,code);
//        // 2.2 根据时间获取数据
        List<Stock4EvrDayDomain> list = stockRtInfoMapper.getStockKDayByTime(code,lastTimeByDay);

        // 3. 返回数据
        return R.ok(list);

    }





}
